<template>
  <div class="ag-grid">
    <ag-grid-vue
      ref="grid"
      :style="`height:100%;width:100%`"
      class="ag-theme-alpine"
      :column-defs="columnDefs"
      :context="context"
      :row-data="data"
      :grid-options="gridOptions"
      :column-types="columnTypes"
      :default-col-def="defaultColDef"
      :row-selection="rowSelection"
      :pinned-bottom-row-data="pinnedBottomRowData"
      :suppress-cell-selection="!editable"
      :framework-components="frameworkComponents"
      :suppress-keyboard-event="suppressKeyboardEvent"
      :default-page-size="paginationPageSize"
      :get-context-menu-items="getContextMenuItems"
      :enable-range-selection="true"
      :allow-context-menu-with-control-key="true"
      :suppress-pagination-panel="true"
      @grid-ready="onGridReady"
      @row-clicked="onRowClicked"
      @cell-key-down="onCellKeyDown"
      @cell-key-press="onCellKeyPress"
      @cell-value-changed="onCellValueChanged"
      @selection-changed="onSelectionChanged"
    />
    <pagination
      ref="pagination"
      :grid-api="gridApi"
      :total="total"
      :default-page-size="paginationPageSize"
      @size-change="handlePageSizeChange"
      @page-change="handlePageChange"
    />
    <!-- <vue-context ref="contextMenu">
      <ul>
        <li @click="handleCopyCell">自定义</li>
      </ul>
    </vue-context> -->
  </div>
</template>

<script>
import { AgGridVue } from 'ag-grid-vue'
import NP from 'number-precision'
import Pagination from './Pagination'
// import VueContext from 'vue-context'
import OperationCellRenderer from '../components/OperationCellRenderer'
import DatePickerCellEditor from '../components/DatePickerCellEditor'
import NumberInputCellEditor from '../components/NumberInputCellEditor'
import StringInputCellEditor from '../components/StringInputCellEditor'
import BooleanCellRenderer from '../components/BooleanCellRenderer'
import SelectionCellEditor from '../components/SelectionCellEditor'
import IntegerNumberInputCellEditor from '../components/IntegerNumberInputCellEditor'

export default {
  name: 'ServerTable',
  components: {
    AgGridVue,
    Pagination,
    // VueContext,
    /* eslint-disable */
    operationRenderer: OperationCellRenderer,
    datePickerEditor: DatePickerCellEditor,
    booleanRenderer: BooleanCellRenderer,
    selectionEditor: SelectionCellEditor
    /* eslint-enable */
  },
  props: {
    // 表格数据
    columns: {
      type: Array,
      default: undefined
    },
    rowData: {
      type: Object,
      default: undefined
    },
    // 获取每页数据 返回一个Promise
    getPageData: {
      type: Function,
      default: undefined
    },
    // 修改
    update: {
      type: Function,
      default: undefined
    },
    // 删除
    delete: {
      type: Function,
      default: undefined
    },
    // 撤销
    revoke: {
      type: Function,
      default: undefined
    },
    // 回调方法
    operation: {
      type: Boolean,
      default: false
    },
    // 是否显示tooltip
    tooltip: {
      type: Boolean,
      default: true
    },
    // 是否开启列属性转换 -- 适配ErpCommonTable的列定义
    transformColumn: {
      type: Boolean,
      default: false
    },
    // 合计行
    showSummary: {
      type: Boolean,
      default: false
    },
    // 自定义的合计计算回调，可拿到(column,data)
    summaryMethod: {
      type: Function,
      default: undefined
    },
    // 当前行的最后一个可编辑cell Enter时
    doEnterLastCellOfRow: {
      type: Function,
      default: undefined
    },
    // 表格最后一个可编辑cell Enter时
    doEnterLastCell: {
      type: Function,
      default: undefined
    }
  },
  data() {
    return {
      data: [],
      gridApi: null,
      gridColumnApi: null,
      // 列类型
      columnTypes: null,
      // 默认勾选
      rowSelection: null,
      // 默认列定义
      defaultColDef: {
        initialWidth: 80, // 初始宽度
        sortable: true,
        resizable: true
      },
      // 表格配置属性
      gridOptions: {
        headerHeight: 30, // 表头行高
        rowHeight: 30, // 表体行高
        resizable: true, // 列宽启动自适应
        sortable: true // 开启排序
      },
      // 表格上下文 用来给params附加内容
      context: null,
      // 初始分页大小
      limit: {
        page: 1,
        pageSize: 100
      },
      total: 0, // 总数据条数
      // 每页显示条数
      paginationPageSize: 100,
      // 合计行
      pinnedBottomRowData: null,
      // 隐藏/显示操作列
      suppressHover: 1000,
      showOperation: true,
      // 单击延时器,处理双击单击冲突
      click_time: null,
      onClickFlag: false,
      // 自定义组件
      frameworkComponents: {
        stringInputCellEditor: StringInputCellEditor,
        numberInputCellEditor: NumberInputCellEditor,
        integerNumberInputCellEditor: IntegerNumberInputCellEditor
      }
    }
  },
  computed: {
    // 使列属性合法
    validColumns() {
      let validColumns = this.columns
      if (this.transformColumn && this.columns) { // 适配ErpCommonTable列设置
        validColumns = this.columns.map(column => {
          const newColumn = Object.assign({}, column)
          newColumn.headerName = column.headerName || column.name
          newColumn.field = column.field || column.prop
          newColumn.type = column.type
          newColumn.showSummary = column.showSummary
          if (newColumn.type) {
            newColumn.type = Array.isArray(newColumn.type)
              ? [...newColumn.type, 'numericColumn'] : [newColumn.type, 'numericColumn']
          } else {
            newColumn.type = 'numericColumn'
          }
          newColumn.width && delete newColumn.width
          return newColumn
        })
      }
      // 添加表头和单元格tooltip
      validColumns.forEach(column => {
        column.headerTooltip = column.headerName
        column.tooltipValueGetter = params => {
          return params.value
        }
      })
      return validColumns
    },
    // 编辑型表格
    editable() {
      return this.validColumns.some(column => column.editable ||
        ['stringColumn', 'numberColumn', 'integerNumberColumn', 'dateColumn', 'selectionColumn', 'tableInputColumn', 'cascaderColumn'].includes(column.type))
    },
    // 左固定
    preColumnDefs() {
      return [
        {
          colId: 'selectColumn',
          maxWidth: 55,
          sortable: false,
          filter: false,
          rowDrag: false,
          pinned: 'left',
          suppressMenu: true,
          suppressSizeToFit: true,
          checkboxSelection: true,
          headerCheckboxSelection: true,
          headerCheckboxSelectionFilteredOnly: true,
          cellClass: 'selectColumn'
        },
        {
          headerName: '#',
          colId: 'indexColumn',
          maxWidth: 60,
          valueGetter: (params) => {
            const rowIndex = params.node.rowIndex
            return (this.limit.page - 1) * this.limit.pageSize + rowIndex + 1
          },
          sortable: false,
          filter: false,
          pinned: 'left',
          suppressMenu: true,
          suppressSizeToFit: true,
          cellClass: 'indexColumn',
          cellStyle: { textAlign: 'left' }
        }
      ]
    },
    // 操作列
    operationColumn() {
      return {
        headerName: '操作',
        maxWidth: 130,
        colId: 'operationColumn',
        sortable: false,
        filter: false,
        pinned: 'right',
        rowDrag: false,
        suppressMenu: true,
        suppressSizeToFit: true,
        suppressMovable: false,
        cellRenderer: 'operationRenderer'
      }
    },
    // 右固定
    postColumnDefs() {
      return this.operation ? [this.operationColumn] : []
    },
    // 表格渲染数据
    columnDefs() {
      const columnDefs = []
      columnDefs.push(...this.preColumnDefs, ...this.validColumns, ...this.postColumnDefs)
      return columnDefs
    }
  },
  watch: {
    'rowData': function(val) {
      if (val) {
        this.data = val.pageItems
        this.total = val.total
        this.gridApi.clearRangeSelection()
        if (this.showSummary) {
          this.syncSummaryRow(this.data)
        }
        this.setColumnWidth()
      }
    }
  },
  beforeMount() {
    // 数据类型
    this.columnTypes = {
      // 布尔
      booleanColumn: {
        maxWidth: 80,
        cellRenderer: 'booleanRenderer',
        cellRendererParams: {
          trueLabel: 1,
          falseLabel: 0
        }
      },
      linkColumn: {
        cellRenderer: 'linkRenderer'
      },
      progressColumn: {
        maxWidth: 200,
        cellRenderer: 'progressCellShow'
      },
      degreeColumn: {
        maxWidth: 200,
        cellRenderer: 'degreeCellShow'
      },
      // 字符输入框
      stringColumn: {
        editable: true,
        cellEditor: 'stringInputCellEditor',
        cellStyle: { color: 'blue' }
      },
      // 数字输入框
      numberColumn: {
        editable: true,
        cellEditor: 'numberInputCellEditor',
        cellStyle: { color: 'blue' }
      },
      cascaderColumn: {
        editable: true,
        cellEditor: 'LyCascader',
        cellStyle: { color: 'blue' },
        valueFormatter: params => {
          // 级联选择器 多层结构需要在父组件中组合成单层数组另命名,以防止结构冲突
          if (!params.colDef.cellEditorParams.props) return params.value
          const labelProp = params.colDef.cellEditorParams.props.label
          const valueProp = params.colDef.cellEditorParams.props.value
          const item1 = params.colDef.cellEditorParams.list.find(item => item[valueProp] === params.value)
          const backup = params.colDef.cellEditorParams.props.backup
          var item2
          params.colDef.cellEditorParams.data ? item2 = params.colDef.cellEditorParams.data.find(item => item[valueProp] === params.value) : item2 = null
          if (item1) return item1[labelProp]
          else if (item2) return item2[backup]
          else return params.value
        }
      },
      // 输入框
      integerNumberColumn: {
        editable: true,
        cellEditor: 'integerNumberInputCellEditor',
        cellStyle: { color: 'blue' }
      },
      // 时间
      dateColumn: {
        editable: true,
        cellEditor: 'datePickerEditor',
        cellStyle: { color: 'blue' }
      },
      selectionColumn: {
        editable: true,
        cellEditor: 'selectionEditor',
        cellStyle: { color: 'blue' },
        valueFormatter: params => {
          if (!params.colDef.cellEditorParams.props) return params.value
          const labelProp = params.colDef.cellEditorParams.props.label
          const valueProp = params.colDef.cellEditorParams.props.value
          const item = params.colDef.cellEditorParams.list.find(item => item[valueProp] === params.value)
          return item ? item[labelProp] : params.value
        }
      },
      tableInputColumn: {
        editable: true,
        cellEditor: 'tableInputEditor',
        cellStyle: { color: 'blue' },
        valueFormatter: params => {
          if (!params.colDef.cellEditorParams.props) return params.value
          const labelProp = params.colDef.cellEditorParams.props.label
          const valueProp = params.colDef.cellEditorParams.props.value
          const item = params.colDef.cellEditorParams.list && params.colDef.cellEditorParams.list.find(item => item[valueProp] === params.value)
          return item ? item[labelProp] : params.value
        }
      },
      newTableInputColumn: {
        editable: true,
        cellEditor: 'newTableInputEditor',
        cellStyle: { color: 'blue' },
        valueFormatter: params => {
          if (!params.colDef.cellEditorParams.props) return params.value
          const labelProp = params.colDef.cellEditorParams.props.label
          const valueProp = params.colDef.cellEditorParams.props.value
          const item = params.colDef.cellEditorParams.list && params.colDef.cellEditorParams.list.find(item => item[valueProp] === params.value)
          return item ? item[labelProp] : params.value
        }
      },
      tableInputPagingColumn: {
        editable: true,
        cellEditor: 'newTableInputPaging',
        cellStyle: { color: 'blue' },
        valueFormatter: params => {
          if (!params.colDef.cellEditorParams.props) return params.value
          const labelProp = params.colDef.cellEditorParams.props.label
          const valueProp = params.colDef.cellEditorParams.props.value
          const item = params.colDef.cellEditorParams.list && params.colDef.cellEditorParams.list.find(item => item[valueProp] === params.value)
          return item ? item[labelProp] : params.value
        }
      }
    }
    // 按钮
    this.context = {
      componentParent: this
    }
    // 默认勾选
    this.rowSelection = 'multiple'
  },
  methods: {
    // 初始化
    onGridReady(params) {
      this.gridApi = params.api
      this.gridColumnApi = params.columnApi
      this.gridApi.clearRangeSelection()
      if (this.showSummary) {
        this.syncSummaryRow(this.getData())
      }
      this.setColumnWidth()
    },

    // API
    refresh() {
      // 清除选中数据
      this.gridOptions.api.deselectAll()
      // 重定页数
      this.$refs.pagination.resetCurrentPage()
      // 查询数据
      this.$parent.getPageData()
    },
    // 获取所有数据
    getData(filter, sort) {
      const data = []
      if (this.gridApi) {
        if (filter && sort) {
          this.gridApi.forEachNodeAfterFilterAndSort(node => {
            data.push(node.data)
          })
        } else if (filter) {
          this.gridApi.forEachNodeAfterFilter(node => {
            data.push(node.data)
          })
        } else {
          this.gridApi.forEachNode(node => {
            data.push(node.data)
          })
        }
      }
      return data
    },
    // 清除范围选择
    clearRangeSelection() {
      this.gridApi.clearRangeSelection()
    },
    // 获取每页数据
    pageData() {
      return this.limit
    },
    // 添加行
    addRows(rows, index) {
      const transaction = { add: rows, addIndex: index }
      this.gridApi.applyTransaction(transaction)
    },
    // getDate
    updateRows(rows) {
      const transaction = { update: rows }
      this.gridApi.applyTransaction(transaction)
    },
    // 删除行
    removeRows(rows) {
      const transaction = { remove: rows }
      this.gridApi.applyTransaction(transaction)
    },
    // 重绘表格内容
    redrawRows(rows) {
      this.gridApi.redrawRows({ rows })
    },
    // 点击事件
    onRowClicked(e) {
      if (e.rowPinned === 'bottom') return
      this.onClickFlag = false
      this.$emit('row-click', e.data, e)
      this.click_time = setTimeout(() => {
        // 非编辑状态下才触发事件
        const editing = e.api.getEditingCells()
        if (editing.length === 0 && !this.onClickFlag) {
          this.$emit('current-row', e.data, e.node)
        }
      }, 300)
    },

    // Methods
    onCellKeyPress(params) {
      const { event, value } = params
      if ((event.ctrlKey || event.metaKey) && (event.key === 'c' || event.key === 'C')) {
        // 阻止默认事件，以防止浏览器复制
        event.preventDefault()
        // 复制操作
        this.handleCopyCell(value)
      }
    },
    handleCopyCell(text) {
      const textArea = document.createElement('textarea')
      textArea.value = text
      document.body.appendChild(textArea)
      textArea.select()
      document.execCommand('copy')
      document.body.removeChild(textArea)
    },
    // 右键自定义
    getContextMenuItems(params) {
      return ['copy', 'paste', 'export']
      // return params.defaultItems.concat()
    },
    // 回车事件
    onCellKeyDown(e) {
      // 默认模式 不做处理
      if (e.colDef.nextCellDisabled) return
      const keyPressed = e.event.key
      if (keyPressed === 'Enter') {
        const cell = { rowIndex: e.rowIndex, column: e.column, rowPinned: e.rowPinned }
        const nextCell = this.getNextEditableCell(cell)
        if (!nextCell || nextCell.rowIndex !== cell.rowIndex) {
          this.doEnterLastCellOfRow && this.doEnterLastCellOfRow(cell, e.data, this.doTab, e)
          this.doEnterLastCell ? this.doEnterLastCell(cell, e.data, this.doTab, e) : this.gridApi.clearFocusedCell()
          if (this.addRow) {
            this.$emit('add-row')
            this.gridApi.setFocusedCell(e.rowIndex, e.column, null)
          }
          return
        }
        this.gridApi.setFocusedCell(nextCell.rowIndex, nextCell.column.getColId(), nextCell.rowPinned)
      }
    },
    // 获取下一个可编辑的cell
    getNextEditableCell(gridCell) {
      let nextCell = gridCell
      let cellIsEditable = false
      while (!cellIsEditable) {
        nextCell = this.getNextCell(nextCell)
        if (!nextCell) {
          break
        }
        const rowNode = this.gridApi.getDisplayedRowAtIndex(nextCell.rowIndex)
        cellIsEditable = nextCell.column.isCellEditable(rowNode)
      }
      return nextCell
    },
    // 获取下一个cell
    getNextCell(gridCell) {
      const displayedColumns = this.gridColumnApi.getAllDisplayedColumns()
      let newRowIndex = gridCell.rowIndex
      let newFloating = gridCell.rowPinned
      // move along to the next cell
      let newColumn = this.gridColumnApi.getDisplayedColAfter(gridCell.column)
      if (!newColumn) {
        newColumn = displayedColumns[0]
        const rowBelow = this.getRowBelow(gridCell)
        if (!rowBelow) {
          return null
        }
        newRowIndex = rowBelow ? rowBelow.rowIndex : null
        newFloating = rowBelow ? rowBelow.rowPinned : null
      }
      return { rowIndex: newRowIndex, column: newColumn, rowPinned: newFloating }
    },
    // 获取下一行
    getRowBelow(gridCell) {
      const index = gridCell.rowIndex
      const pinned = gridCell.rowPinned
      if (pinned) return { rowIndex: index, rowPinned: pinned }
      const lastIndex = this.gridApi.getLastDisplayedRow()
      if (index === lastIndex) {
        return null
      }
      return { rowIndex: index + 1, rowPinned: pinned }
    },
    // 双击修改
    onCellValueChanged(e) {
      this.$emit('cell-value-changed', e)
    },
    // 选中行
    onSelectionChanged(e) {
      if (!e.api) return
      const selectedRowNodes = e.api.getSelectedNodes().sort((a, b) => {
        return a.rowIndex - b.rowIndex
      }).slice(0, this.paginationPageSize)
      const selectedRows = selectedRowNodes.map(node => node.data)
      this.$emit('selection-change', selectedRows, selectedRowNodes)
    },
    // 接受分页子组件传来的分页大小 limit 10 50 100 500 1000
    handlePageSizeChange(pageSize) {
      this.limit.pageSize = pageSize
      this.paginationPageSize = pageSize
      this.loadData()
    },
    handlePageChange(page) {
      this.limit.page = page
      this.loadData()
    },
    loadData() {
      this.$parent.getPageData(this.limit)
    },
    // 隐藏悬浮文字
    suppressKeyboardEvent(params) {
      if (params.event.type === 'keydown') {
        const tooltipElements = document.querySelectorAll('.ag-tooltip')
        tooltipElements.forEach((element) => {
          element.style.display = 'none'
        })
      }
      return false
    },
    // 同步更新合计行 rows => 要统计的合计行
    syncSummaryRow(rows) {
      if (this.gridApi && this.showSummary && rows) {
        const allColumns = this.gridColumnApi.getAllColumns()
        const centerColumns = this.gridColumnApi.getDisplayedCenterColumns()
        if (centerColumns && centerColumns.length > 0) {
          // 准备列
          const firstCenterColumn = centerColumns[0]
          const summaryColumns = allColumns.filter(column => column.getColDef().showSummary && column.getColId() !== firstCenterColumn.getColId())
          const uniqueSummaryFields = [...new Set(summaryColumns.map(column => column.getColDef().field))]
          const summaryRowData = uniqueSummaryFields.reduce((summary, field) => {
            if (this.summaryMethod) {
              summary[field] = this.summaryMethod(field, rows)
            } else {
              summary[field] = rows.reduce((acc, curr) => {
                const value = Number(curr[field])
                if (!isNaN(value)) {
                  return NP.plus(acc, value)
                } else {
                  return acc
                }
              }, 0)
            }
            return summary
          }, {})
          summaryRowData[firstCenterColumn.getColDef().field] = '合计'
          this.pinnedBottomRowData = [summaryRowData]
        }
      }
    },
    // 自适应宽度
    setColumnWidth() {
      this.$nextTick(() => {
        const allColumnIds = []
        this.gridColumnApi.getColumns().forEach((column) => {
          allColumnIds.push(column.getId())
        })
        this.gridColumnApi.autoSizeColumns(allColumnIds, false)
      }, 500)
    }

  }
}
</script>

<style lang="scss">
   @import "~ag-grid-community/styles/ag-grid.css";
   @import "~ag-grid-community/styles/ag-theme-alpine.css";

.ag-grid{
  height: 100%;
}

// 表头背景
.ag-header {
  background-color: #F7F9FE !important;
}

// 偶数表体背景
.ag-row-odd{
  background-color: #eff7fe !important;
}

// 合计固定页脚样式问题
.ag-pinned-left-floating-bottom{
  .ag-row-even{
    .indexColumn{
      color: transparent;
    }
  }
}
.ag-floating-bottom{
  .operation-cell{
    display: none !important;
  }
}

// 表体border
.ag-ltr .ag-cell{
  border-right: 1px solid #bdc3c7 !important;
}
.ag-pinned-right-header .ag-header-cell-resize::after{
  left: 0%;
}
[col-id="indexColumn"]{
  .ag-header-cell-resize::after{
    height: 0px !important;
  }
}

// 表体点击border颜色
.ag-ltr .ag-cell-focus:not(.ag-cell-range-selected):focus-within{
  border-color: #2196f3 !important;
}

// 表头右border
.ag-header-cell-resize::after{
  top:8px !important;
  width: 1px !important;
  height: 50% !important;
}

// 必填色
.my-class {
  color: red !important;
}

// 表格内容样式
.class-center {
  text-align: left;
}

// 审核颜色
.redColor {
  color: red;
}
.greenColor {
  color: green;
}
</style>
